#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>     //for getopt, fork
#include <string.h>     //for strcat
//for struct evkeyvalq
#include <sys/queue.h>
#include <event.h>
//for http
//#include <evhttp.h>
#include <event2/http.h>
#include <event2/http_struct.h>
#include <event2/http_compat.h>
#include <event2/util.h>
#include <signal.h>
#include <sqlite3.h>
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <mutex>

#include "json.hpp"
//#include "threadsafe_unordered_map.h"
#define MYHTTPD_SIGNATURE   "myhttpd v 0.0.1"

using namespace std;
//sqlite3* db;
mutex MsgLock;
mutex SessionLock;
mutex QALock;
vector<string> MsgVec;
unordered_map<string, string> SessionMap; 
unordered_map<string, vector<string>> QuestionMap;
unordered_map<string, vector<char>> AnswerMap;

int select_callback(void* data, int argc, char** argv, char** azColName){
	cout << "select" <<endl;
	MsgLock.lock();
	MsgVec.clear();
	if(argc == 0) {
		MsgLock.unlock();
		return -1;
	}
	for(int i = 0; i < argc; i++) {
		MsgVec.push_back(argv[i]);
	}
	MsgLock.unlock();
	return 0;
}

vector<string> CasesVec;
int select_case_callback(void* data, int argc, char** argv, char** azColName){
	cout << "select * from cases;" << endl;
	if(argc == 0) {
		return -1;
	}	
	string tmp = argv[1]; 
	tmp = tmp + "\t";
	string tt = argv[2];
	tt += "\t";
	string tt2 = argv[0];
	tt2 += "\n";
	tmp += tt;
	tmp += tt2;
	CasesVec.push_back(tmp);
	return 0;
}
int del_case_callback(void* data, int argc, char** argv, char** azColName){
	return 0;
}

int insert_case_callback(void* data, int argc, char** argv, char** azColName){
	return 0;
}

vector<string> UsersVec;
int select_user_callback(void* data, int argc, char** argv, char** azColName){
	//cout << "select * from users;" << endl;
	cout << argc << endl;	
	string tmp;
	cout << (string)argv[0] << " " << (string)argv[1] << endl;
	tmp = (string)argv[0] + "\t" + (string)argv[1] + "\n";
	UsersVec.push_back(tmp);
	return 0;
}

int del_user_callback(void* data, int argc, char** argv, char** azColName){
	return 0;
}

vector<string> ContestsVec;
int select_contest_callback(void* data, int argc, char** argv, char** azColName){
	cout << "select * from contest;" << endl;
	string tmp;
	tmp = (string)argv[0] + "\t" + (string)argv[1] + "\t" + (string)argv[2] + "\t" + (string)argv[3] + "\n";
	ContestsVec.push_back(tmp);	
	return 0;
}

int del_contest_callback(void* data, int argc, char** argv, char** azColName){
	return 0;
}

int insert_contest_callback(void* data, int argc, char** argv, char** azColName){
	return 0;
}

int insert_callback(void* data, int argc, char** argv, char** azColName){
	if(argc == 0)
		return 0;
	else 
		return -1;	
}

int generate_callback(void* data, int argc, char** argv, char** azColName){
	string username = *(string*) data;
	//cout << username << endl;
	if(argc!=2) return -1;
	char* content = argv[0];
	char* answer = argv[1];
	QuestionMap[username].push_back(content);
	AnswerMap[username].push_back(*(answer));
	return 0;
}

void session_del(int sock, short event, void* arg){
	cout << "session_del:" << *(string*)arg << endl;
	SessionMap.erase(*(string*)arg);
	delete (string*)arg;
}

//struct event session_timer;
//处理模块
void httpd_handler(struct evhttp_request *req, void *arg) {
    char output[4096] = "\0";
    char tmp[1024];
    std::string method, username, password;
    //获取客户端请求的URI(使用evhttp_request_uri或直接req->uri)
    const char *uri;
    uri = evhttp_request_uri(req);
    sprintf(tmp, "uri=%s\n", uri);
    strcat(output, tmp);

    sprintf(tmp, "uri=%s\n", req->uri);
    strcat(output, tmp);
    //decoded uri
    char *decoded_uri;
    decoded_uri = evhttp_decode_uri(uri);
    sprintf(tmp, "decoded_uri=%s\n", decoded_uri);
    strcat(output, tmp);
	method = decoded_uri;
	for(int i = 0; i < method.size(); i++) {
		if(method[i] == '?') {
			method = method.substr(1, i-1);
			break;
		}
	}
    //解析URI的参数(即GET方法的参数)
    struct evkeyvalq params;
    //将URL数据封装成key-value格式,q=value1, s=value2
    evhttp_parse_query(decoded_uri, &params);
    //得到q所对应的value
    //sprintf(tmp, "q=%s\n", evhttp_find_header(&params, "username"));
    //strcat(output, tmp);
    //得到s所对应的value
    //sprintf(tmp, "s=%s\n", evhttp_find_header(&params, "password"));
    //strcat(output, tmp);
	int res = 0;
	float ans = 0;
	sqlite3* db;
	sqlite3_open("/home/work/PetHospital.db", &db);
	char* msg;
	char* ErrMsg;
	if(method == "login") {
		username = evhttp_find_header(&params, "username");
		password = evhttp_find_header(&params, "password");
		std::cout << "method:" << method << "--" << username << ":" << password << std::endl;
		std::string sql = "select password from USERS where username='" + username+ "';";
		char* msg;
		char* ErrMsg;
		std::cout << sql <<std::endl;
		int ret = sqlite3_exec(db, &sql[0], select_callback, 0, &ErrMsg);
		//cout << ret << endl;
		//cout<<MsgVec.size()<<endl;
		//for(int i=0;i<MsgVec.size();i++) cout<<MsgVec[i]<<endl;
		MsgLock.lock();
		if(ret!=0|| MsgVec.size() == 0 || MsgVec[0]!=password) {
			res = -1; // 密码错误
			std::cout << "password error." <<std::endl;
			MsgLock.unlock();
		}
		else { 
			res = 0; //登录成功		
			std::cout << "login success." <<std::endl;
			MsgLock.unlock();
			SessionLock.lock();
			SessionMap.insert(pair<string, string> {username, username});
			SessionLock.unlock();
			string* tmp = new string(username);
			struct event* ev_timer;
			struct timeval* tv = (struct timeval*)malloc(sizeof(timeval));
			tv->tv_sec = 600;
			tv->tv_usec = 0;
			ev_timer = evtimer_new((struct event_base*)arg, session_del, tmp);
			event_add(ev_timer, tv);
		}
		//MsgLock.unlock();
	}
	else if(method == "register") {
		username = evhttp_find_header(&params, "username");
		password = evhttp_find_header(&params, "password");
		std::cout << "method:" << method << "--" << username << ":" << password << std::endl;
		std::string sql = "insert into USERS(username, password) VALUES('" + username + "','" + password + "');";
		char* msg;
		char* ErrMsg;
		int ret = sqlite3_exec(db, sql.c_str(), insert_callback, msg, &ErrMsg);
		if(ret!=0||ErrMsg!=NULL){
			res = 2; //注册失败
			cout << "register failed." << endl;
		}	
		else {
			res = 1; //注册成功
			cout << "register success." << endl;
		}
	}
	else if(method == "check") {
		username = evhttp_find_header(&params, "username");
	//	password = evhttp_find_header(&params, "password");
		std::cout << "method:" << method << "--" << username << std::endl;
		SessionLock.lock();
		if(SessionMap.find(username) != SessionMap.end()) {
			res = 3; //状态有效
			cout << "session check success." << endl;
		}	
		else {
			res = 4; //状态无效
			cout << "session check failed." << endl;
		}
		SessionLock.unlock();
	}
	else if(method == "contest") {
		username = evhttp_find_header(&params, "username");
		QuestionMap[username].clear();
		AnswerMap[username].clear();
		cout << username << endl;
		string math_num = evhttp_find_header(&params, "math");
		string phy_num = evhttp_find_header(&params, "physical");
		//int n = stoi(math_num) + stoi(phy_num);
		//string number = to_string(n);
		cout << "math number: " << math_num << "physical number: " << phy_num << endl;
		//std::cout << "method:" << method << "--" << "math:" << math_num << std::endl;
		std::string sql = "select content, answer from contest where title='math' order by random() limit " + math_num + ";";
		std::string sql2 = "select content, answer from contest where title='physical' order by random() limit " + phy_num + ";";
		string* msg = &username;
		char* ErrMsg;
		int ret = sqlite3_exec(db, sql.c_str(), generate_callback, (char*)msg, &ErrMsg);
	 	//cout << ret << endl;	
		ret = ret || sqlite3_exec(db, sql2.c_str(), generate_callback, (char*)msg, &ErrMsg);
		if (ret!=0) {
			res = 5; //生成失败
			cout << "generate failed." << endl;
		}
		else {
			res = 6;
			cout << "generate success." << endl;
		}
	}
	else if(method == "answer"){
		username = evhttp_find_header(&params, "username");
		string answer = evhttp_find_header(&params, "answer");
		int fault = 0;
		int len = answer.size();
		for (int iter = 0; iter != AnswerMap[username].size(); ++iter) {
			if (answer[iter] != AnswerMap[username][iter]){
				fault++;			
			}
		}
		res = 7;
		ans = 1.0 * fault / len;
		ans = 100 - ans * 100;
	}
	else if(method == "monitor"){
		string _case = evhttp_find_header(&params, "case");
		if(_case == "1") {
			// 显示所有用户
			// 19
			UsersVec.clear();
			int ret = sqlite3_exec(db, "select * from USERS;", select_user_callback, (char*)msg, &ErrMsg);
			res = 19;		
		}
		else if(_case == "2") {
			//新增用户
			// s 7
			// 	f 8
			// 走注册的请求	
		}	
		else if(_case == "3") {
			//删除用户
			// s 9
			// f 10
			string id = evhttp_find_header(&params, "username");
			string _sql = "DELETE from USERS where username = '" + id + "';";
			int ret = sqlite3_exec(db, _sql.c_str(), del_user_callback, (char*)msg, &ErrMsg);
			if(ret != 0)
				res = 10;
			else
				res = 9;		
		}
		else if(_case == "4") {
			// 显示所有病例
			// 20
			CasesVec.clear();
			int ret = sqlite3_exec(db, "select * from CASES;", select_case_callback, (char*)msg, &ErrMsg);
			res = 20;					
		}
		else if(_case == "5") {
			//新增病例
			// s 11
			// f 12
			// [[title, step1, step2, step3, step4]]
			string title = evhttp_find_header(&params, "title");
			cout << title << endl;
			string content = evhttp_find_header(&params, "content");
			cout << content << endl;
			string _sql = "insert into CASES values(null, '" + title + "','" + content + "');";
			int ret = sqlite3_exec(db, _sql.c_str(), insert_case_callback, (char*)msg, &ErrMsg);
			if (ret != 0)
				res = 12;
			else
				res = 11;
		}
		else if(_case == "6") {
			//删除病例
			// s 13
			// f 14
			string id = evhttp_find_header(&params, "id");
            string _sql = "DELETE from CASES where id = " + id + ";";
            int ret = sqlite3_exec(db, _sql.c_str(), del_case_callback, (char*)msg, &ErrMsg);
             if(ret != 0)
                 res = 14;
             else
                 res = 13;
		}
		else if(_case == "7") {
			//显示所有测试题
			// 21
			ContestsVec.clear();
			int ret = sqlite3_exec(db, "select * from contest;", select_contest_callback, (char**)msg, &ErrMsg);
			res = 21;
		}
		else if(_case == "8") {
			//新增测试试题
			// s 15
			// f 16
			string cls = evhttp_find_header(&params, "class");
			string content = evhttp_find_header(&params, "content");
			string result = evhttp_find_header(&params, "result");
			string _sql = "insert contest value(null, '" + cls + "', '" + content + "','" + result + "');";
			int ret = sqlite3_exec(db, _sql.c_str(), insert_contest_callback, (char*)msg, &ErrMsg);
			if(ret != 0)
				res = 15;
			else
				res = 16;		
		}
		else if(_case == "9") {
			//删除测试试题
			// s 17
			// f 18
			string id = evhttp_find_header(&params, "id");
            string _sql = "DELETE from contest where id = " + id + ";";
            int ret = sqlite3_exec(db, _sql.c_str(), del_contest_callback, (char*)msg, &ErrMsg);
             if(ret != 0)
                 res = 18;
             else
                 res = 17;
		}
	}
	else if(method == "form"){
		//char *post_data = (char *) EVBUFFER_DATA(req->input_buffer);
		//printf("post_data=%s\n", post_data);
	}
	else{
		res = -2;
		cout << "method undefined." << endl;
	}

	char *post_data = (char *) EVBUFFER_DATA(req->input_buffer);
	//evbuffer_free(req->input_buffer);
	do {
	if (post_data != NULL) {
	//	printf("post_data=%s\n", post_data);
		string tmp = post_data;
		cout << tmp << endl;
		int _b = -1;
		int _e = -1;
		for(int i = 0;i < tmp.size(); ++i) {
			if(tmp[i]=='{')
				_b = _b==-1?i:_b;
			else if(tmp[i]=='}') 
				_e = _e==-1?i:_e;
		}
		if (_b == -1 || _e == -1 || _b >= _e) {
			cout << "something wrong." << endl;
			res = 99;
			break;
		}	
		tmp = tmp.substr(_b, (_e - _b + 1));
		nlohmann::json json = nlohmann::json::parse(tmp);
	//	cout << json << endl;
	//	for(auto iter=json["case"].begin(); iter!=json["case"].end(); ++iter) {
	//		cout << *iter << endl;
	//	}
	//	cout << json["case"] << endl;
	//	cout << json["title"] << endl;
	//	cout << json["content"] << endl;
		if(json["case"] == 1) {
				string title = json["title"];
				cout << title << endl;
				string content = json["content"];
				cout << content << endl;
				string _sql = "insert into CASES values(null, '" + title + "','" + content + "');";
				int ret = sqlite3_exec(db, _sql.c_str(), insert_case_callback, (char*)msg, &ErrMsg);
				if (ret != 0)
					res = 12;
				else
					res = 11;
		}
		else{
			string cls = json["class"];
			string content = json["content"];
			string result = json["answer"];
			string _sql = "insert into contest values(null, '" + cls + "', '" + content + "','" + result + "');";
			int ret = sqlite3_exec(db, _sql.c_str(), insert_contest_callback, (char*)msg, &ErrMsg);
			if(ret != 0)
				res = 15;
			else
				res = 16;		

		}
	}}while(0);
	sqlite3_close(db);
    free(decoded_uri);

    //获取POST方法的数据
    //char *post_data = (char *) EVBUFFER_DATA(req->input_buffer);
    //sprintf(tmp, "post_data=%s\n", post_data);
    //strcat(output, tmp);

    /*
       具体的：可以根据GET/POST的参数执行相应操作，然后将结果输出
       ...
     */

    /* 输出到客户端 */

    //HTTP header
    evhttp_add_header(req->output_headers, "Server", MYHTTPD_SIGNATURE);
    evhttp_add_header(req->output_headers, "Content-Type", "text/plain; charset=UTF-8");
    evhttp_add_header(req->output_headers, "Connection", "close");
    //输出的内容
    struct evbuffer *buf;
    buf = evbuffer_new();
    evbuffer_add_printf(buf, "code=%d\n", res);
    if(res == 6) {
		QALock.lock();
		for(int i = 0; i < QuestionMap[username].size(); i++) {
			evbuffer_add_printf(buf, "%s\n", QuestionMap[username][i].c_str());
		}
		QALock.unlock();
	}
	if(res == 7) {
		evbuffer_add_printf(buf, "\n%f\n", ans);
	}
	if(res == 19) {
		for(int i = 0; i < UsersVec.size(); ++i) {
			evbuffer_add_printf(buf, "%s", UsersVec[i].c_str());
		}
	}
	if(res == 20) {
		for(int i = 0; i < CasesVec.size(); ++i) {
			evbuffer_add_printf(buf, "%s", CasesVec[i].c_str());
		}
	}
	if(res == 21) {
		for(int i = 0; i < ContestsVec.size(); ++i) {
			evbuffer_add_printf(buf, "%s", ContestsVec[i].c_str());
		}
	}	
	evhttp_send_reply(req, HTTP_OK, "OK", buf);
    evbuffer_free(buf);

}
void show_help() {
    char *help = "http://localhost:8080\n"
        "-l <ip_addr> interface to listen on, default is 0.0.0.0\n"
        "-p <num>     port number to listen on, default is 1984\n"
        "-d           run as a deamon\n"
        "-t <second>  timeout for a http request, default is 120 seconds\n"
        "-h           print this help and exit\n"
        "\n";
    fprintf(stderr,"%s",help);
}
//当向进程发出SIGTERM/SIGHUP/SIGINT/SIGQUIT的时候，终止event的事件侦听循环
void signal_handler(int sig) {
    switch (sig) {
        case SIGTERM:
        case SIGHUP:
        case SIGQUIT:
        case SIGINT:
            event_loopbreak();  //终止侦听event_dispatch()的事件侦听循环，执行之后的代码
            break;
    }
}

int main(int argc, char *argv[]) {
//	sqlite3* db;
//	init_db(db);

    //默认参数
    char *httpd_option_listen = "0.0.0.0";
    int httpd_option_port = 8080;
    int httpd_option_daemon = 0;
    int httpd_option_timeout = 120; //in seconds

    //获取参数
    int c;
    while ((c = getopt(argc, argv, "l:p:dt:h")) != -1) {
        switch (c) {
            case 'l' :
                httpd_option_listen = optarg;
                break;
            case 'p' :
                httpd_option_port = atoi(optarg);
                break;
            case 'd' :
                httpd_option_daemon = 1;
                break;
            case 't' :
                httpd_option_timeout = atoi(optarg);
                break;
            case 'h' :
            default :
                show_help();
                exit(EXIT_SUCCESS);
        }
    }

    //判断是否设置了-d，以daemon运行
    if (httpd_option_daemon) {
        pid_t pid;
        pid = fork();
        if (pid < 0) {
            perror("fork failed");
            exit(EXIT_FAILURE);
        }
        if (pid > 0) {
            //生成子进程成功，退出父进程
            exit(EXIT_SUCCESS);
        }
    }

    /* 使用libevent创建HTTP Server */

    //初始化event API
    struct event_base *base = event_init();
    //创建一个http server
    struct evhttp *httpd;
    httpd = evhttp_start(httpd_option_listen, httpd_option_port);
    evhttp_set_timeout(httpd, httpd_option_timeout);

    //指定generic callback
    evhttp_set_gencb(httpd, httpd_handler, base);
    //也可以为特定的URI指定callback
    //evhttp_set_cb(httpd, "/", specific_handler, NULL);

    //循环处理events
    event_dispatch();
    evhttp_free(httpd);
	//sqlite3_close(db);
    return 0;
}
